import { GraphQLSchema } from 'graphql'
import { BuiltInParserName } from 'prettier'
import { Config } from '../../config'
import { prettify } from '../../helpers/prettify'
import { relativeImportPath } from './relativeImportPath'

interface Import {
    isDefault: boolean
    module?: string
    alias?: string
}

interface ImportMap {
    [from: string]: Import[]
}

export class RenderContext {
    protected codeBlocks: string[] = []
    protected imports: ImportMap = {}
    protected importAliasCounter = 0

    constructor(public schema?: GraphQLSchema, public config?: Config) {}

    addCodeBlock(block: string) {
        if (block) {
            this.codeBlocks.push(block)
        }
    }

    addImport(
        from: string,
        isDefault: boolean,
        module?: string,
        fromAbsolute?: boolean,
        noAlias?: boolean,
    ) {
        if (this.config && this.config.output) {
            from = fromAbsolute
                ? from
                : relativeImportPath(this.config.output, from)
        }

        if (!this.imports[from]) this.imports[from] = []

        const imports = this.imports[from]

        const existing = imports.find(
            (i) =>
                (isDefault && i.isDefault) ||
                (!isDefault && i.module === module),
        )

        if (existing) return existing.alias

        this.importAliasCounter++
        const alias = noAlias ? undefined : `a${this.importAliasCounter}`
        imports.push({ isDefault, module, alias })

        return alias
    }

    protected getImportBlock() {
        const imports: string[] = []

        Object.keys(this.imports).forEach((from) => {
            let defaultImport = this.imports[from].find((i) => i.isDefault)
            const namedImports = this.imports[from].filter((i) => !i.isDefault)
            const statements: string[] = []

            if (defaultImport) {
                statements.push(defaultImport.alias || '')
            }

            if (namedImports.length > 0) {
                statements.push(
                    `{${namedImports
                        .map((i) =>
                            i.alias ? `${i.module} as ${i.alias}` : i.module,
                        )
                        .join(',')}}`,
                )
            }

            imports.push(`import ${statements.join(',')} from '${from}'`)
        })

        if (imports.length > 0) return imports.join('\n')
        else return
    }

    toCode(parser?: BuiltInParserName, pretty = false) {
        const blocks = [...this.codeBlocks]

        if (parser && (parser === 'typescript' || parser === 'babel')) {
            const importBlock = this.getImportBlock()
            if (importBlock) blocks.unshift(importBlock)
        }
        if (parser && pretty) {
            return prettify(blocks.join('\n\n'), parser)
        }
        if (parser) {
            return blocks.join('\n\n')
        }
        return blocks.join('')
    }
}
